package net.cathackers.devel.scmp.chat.impl.xmpp.iqhandlers;

import org.apache.log4j.Logger;

import net.cathackers.devel.scmp.auth.exceptions.AuthenticationException;
import net.cathackers.devel.scmp.auth.exceptions.LockedUserException;
import net.cathackers.devel.scmp.auth.exceptions.ProxyException;
import net.cathackers.devel.scmp.auth.exceptions.UserNotFoundException;
import net.cathackers.devel.scmp.auth.interfaces.AuthenticationCredentials;
import net.cathackers.devel.scmp.auth.interfaces.AuthenticationInformation;
import net.cathackers.devel.scmp.auth.interfaces.Authenticator;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.AuthenticationHandlerProcessor;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.IQHandlerClassifierImpl;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.interfaces.IQHandler;
import net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent.interfaces.IQHandlerClassifier;
import net.cathackers.devel.scmp.chat.interfaces.ChatManager;
import net.cathackers.devel.scmp.chat.interfaces.ChatSession;
import net.cathackers.devel.scmp.exceptions.BackendException;
import net.cathackers.devel.scmp.factories.SCMPFactory;
import net.cathackers.devel.scmp.xmpp.IQPacket;
import net.cathackers.devel.scmp.xmpp.Packet;

/**
 * Handles jabber:iq:auth protocol (plain only). Clients use this protocol to
 * authenticate with the server. this class is responsible for handling two
 * authentication requests.
 * <ul>
 * get request:
 * </ul>
 * request authentication information for a particular user.
 * <ul>
 * set request:
 * </ul>
 * authenticates with information given in the authentication form.
 * <p>
 * In the case of operation success the server will send result response,
 * otherwise the server will send error response with error message which
 * indicate the type of error and reason.
 */
public class AuthenticationHandler extends IQHandler {
    private ChatManager manager_;
    private Logger logger_;
    private IQHandlerClassifier classifier_;
    private AuthenticationHandlerProcessor handlerProcessor_;

    public AuthenticationHandler() {
	logger_ = SCMPFactory.getLogger(this);
	classifier_ = new IQHandlerClassifierImpl("query", "jabber:iq:auth");
	manager_ = SCMPFactory.getChatManager();
	handlerProcessor_ = new AuthenticationHandlerProcessor();
    }

    /**
     * process IQPackets according to its type(get or set).
     */
    public void process(IQPacket iqPacket) {
	logger_.debug("Authentication requested..");
	// TODO: MAKE SURE THAT WE ARE NOT SENDING THE PASSWORD BACK WHEN THERE
	// ARE ERRORS
	// we are receiving "get" and "set" and we are sending "result" and
	// "error"
	// ( Just a Q,Do we support sessions ?.... No ... ok Cool )
	Packet result = null;
	switch (iqPacket.getIQType()) {
	case get:
	    logger_.debug("Client is querying for Authentication Mechanisms");
	    result = handlerProcessor_
		    .getAutheticationMechanizmsStanza(iqPacket);
	    break;
	case set:
	    AuthenticationCredentials authenticationCredentials = handlerProcessor_
		    .getAuthenticationCredentials(iqPacket);
	    // TODO get an instance of the Authenticator and authenticate the
	    // user and change the dummy implementation
	    // Authenticator authenticator = scmpFactory.getAuthenticator()
	    Authenticator authenticator = SCMPFactory.getAuthenticator();
	    try {
		AuthenticationInformation authInformation = authenticator
			.authenticate(authenticationCredentials);
		if (authInformation == null) {
		    throw new UserNotFoundException();
		} else {
		    String sessionID = iqPacket.getBaseStreamID();
		    ChatSession session = manager_.getChatSession(sessionID);
		    session.setAuthToken(authInformation.getAuthToken());
		    session.setUserID(authInformation.getUserID());
		    session.setStatus(3);
		    if (session.isAuthenticated()
			    && session.getUserID() != null) {
			manager_.identifySession(sessionID);
			logger_.debug("now the session is authenticated  "
				+ sessionID);
		    }
		}

		/**
		 * The client is authenticated, so we will return result packet
		 * to the client.
		 */
		result = handlerProcessor_
			.getSuccessfulAuthenticationStanza(iqPacket);
	    } catch (LockedUserException blockedUserException) {
		result = handlerProcessor_.getBlockedUserErrorStanze(iqPacket);
	    } catch (UserNotFoundException userNotFound) {
		result = handlerProcessor_.getUserNotFoundErrorStanza(iqPacket);
	    } catch (ProxyException proxyException) {
		result = handlerProcessor_
			.getNotAuthorizedErrorStanza(iqPacket);
	    } catch (AuthenticationException e) {
		result = handlerProcessor_
			.getInternalServerErrorStanza(iqPacket);
	    } catch (BackendException e) {
		result = handlerProcessor_
			.getInternalServerErrorStanza(iqPacket);
	    }

	    break;

	default:
	    // TODO
	    // invalidPacket()
	    break;
	}
	result.setBaseStreamID(iqPacket.getBaseStreamID());
	logger_.debug("Setting IQ Authentication Packet Session ID to "
		+ iqPacket.getBaseStreamID());
	manager_.deliverToUser(result);
    }

    @Override
    public IQHandlerClassifier getClassifier() {
	return classifier_;
    }
}